package com.wh.wisdomsite.xbox.core.impl;


import com.wh.wisdomsite.xbox.common.*;
import com.wh.wisdomsite.xbox.common.annotation.NeedInSessionType;
import com.wh.wisdomsite.xbox.common.annotation.IgnoreSignType;
import com.wh.wisdomsite.xbox.common.annotation.ObsoletedType;
import com.wh.wisdomsite.xbox.common.annotation.ServiceMethod;
import com.wh.wisdomsite.xbox.common.annotation.ServiceProvider;
import com.wh.wisdomsite.xbox.common.session.SessionManager;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 保留所有方法和处理器的对应关系(实现XboxContext接口)
 * @Package com.wh.wisdomsite.xbox.core.impl
 * @author 谢泽鹏
 * @date 2014年10月8日 下午4:02:14
 * @Copyright 个人版权所有
 * @Company 贵州万华科技有限公司Copyright (c) 2014
 * @version V1.0
 */
public class DefaultXboxContext implements XboxContext {

    private static final Log logger = LogFactory.getLog(DefaultXboxContext.class);

    /**
     * 所有的服务方法名
     */
    private Set<String> serviceMethods = new HashSet<String>();

    /**
     * 方法  + version ->对应的方法处理器
     */
    private Map<String, ServiceMethodHandler> serviceHandlerMap = new ConcurrentHashMap<String, ServiceMethodHandler>();

    /**
     * 是否开启签名机制
     */
    private boolean signEnable;


    /**
     * 会话管理器
     */
    private SessionManager sessionManager;

    /**
     * 构造函数1
     */
    public DefaultXboxContext() {

    }

    /**
     * 沟站函数2
     * @param applicationContext
     */
    public DefaultXboxContext(ApplicationContext applicationContext) {
        registerFromContext(applicationContext);
    }


    /**
     * 获取SessionManager
     * @return
     */
    @Override
    public SessionManager getSessionManager() {
        return sessionManager;
    }

    /**
     * 设置SessionManager
     * @param sessionManager
     */
    @Override
    public void setSessionManager(SessionManager sessionManager) {
        this.sessionManager = sessionManager;
    }

    /**
     * 是否启用签名
     * @return
     */
    @Override
    public boolean isSignEnable() {
        return signEnable;
    }

    /**
     * 设置启用签名
     * @param signEnable
     */
    @Override
    public void setSignEnable(boolean signEnable) {
        this.signEnable = signEnable;
    }


    /**
     * 添加服务方法
     * @param method  方法名
     * @param version 版本号
     * @param serviceMethodHandler 对象
     */
    @Override
    public void addServiceMethod(String method, String version, ServiceMethodHandler serviceMethodHandler) {
        String key = getMethodKey(method, version);
        if (!serviceHandlerMap.containsKey(key)) {
            serviceHandlerMap.put(key, serviceMethodHandler);
        } else {
            logger.info("addServiceMethod->>Xbox容器已经注册该方法method :" + key + ",不能重复注册！！");
        }
        serviceMethods.add(method);
    }

    /**
     * 通过(方法名+版本号)获取ServiceMethodHandler
     * @param method  方法名
     * @param version 版本号
     * @return
     */
    @Override
    public ServiceMethodHandler getServiceMthodHandler(String method, String version) {
        String key = getMethodKey(method, version);
        return serviceHandlerMap.get(key);
    }

    /**
     * 判断方法是否存在
     * @param method  方法名
     * @return
     */
    @Override
    public boolean containsMethod(String method) {
        return serviceMethods.contains(method);
    }

    /**
     * 验证(方法名+版本号)是否存在
     * @param method  方法名
     * @param version 版本号
     * @return
     */
    @Override
    public boolean isValidMethod(String method, String version) {
        return serviceHandlerMap.containsKey(ServiceMethodDefinition.methodWithVerion(method, version));
    }

    /**
     * 获取所有ServiceHandler的Map集合
     * @return
     */
    @Override
    public Map<String, ServiceMethodHandler> getServiceHandlerMap() {
        return serviceHandlerMap;
    }

    /**
     * 设置所有ServiceHandler的Map集合
     * @param serviceHandlerMap
     */
    public void setServiceHandlerMap(Map<String, ServiceMethodHandler> serviceHandlerMap) {
        this.serviceHandlerMap = serviceHandlerMap;
    }

    /**
     * 获取服务方法
     * @return
     */
    @Override
    public Set<String> getServiceMethods() {
        return serviceMethods;
    }

    /**
     * 设置服务方法
     * @param serviceMethods
     */
    public void setServiceMethods(Set<String> serviceMethods) {
        this.serviceMethods = serviceMethods;
    }

    /**
     * 方法的唯一key ,区别方法
     * @return
     */
    public String getMethodKey(String method, String version) {
        return ServiceMethodDefinition.methodWithVerion(method, version);
    }


    /**
     * 扫描所有标记 @ServiceProvider @ServiceMethod的方法,并注册系统XboxContext中
     * @param applicationContext
     */
    @Override
    public void registerFromContext(final ApplicationContext applicationContext) {
        logger.info("registerFromContext->>扫描spring context->>查找配置注解的@ServiceProvider @ServiceMethod");
        //根据类型装配获取集合beanNames
        String[] beanNames = applicationContext.getBeanNamesForType(Object.class);
        //循环beanNames
        for (final String beanName : beanNames) {
            //根据beanName获取handlerType
            Class handlerType = applicationContext.getType(beanName);
            //spring 提供所有匹配方法上执行给定的回调操作
            ReflectionUtils.doWithMethods(handlerType, new ReflectionUtils.MethodCallback() {
                    @Override
                    public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
                        ReflectionUtils.makeAccessible(method);
                        Class<?> paramType = null;
                        boolean implType = false;

                        /**
                         * 1, 方法匹配拦截
                         */
                        if(!method.getDeclaringClass().getName().matches("[\\w.]*((RegionService)|(ProjectClientService)|(AlarmSignClientService)|(ConfigClientProjectService)|(ShowClientProjectService)|(ConfigClientUserService)|(ConfigClientDictService)|(ConfigClientDeviceService)|(ConfigClientCraneService)|(PublicApiProjectService)|(HkwsService_GA)|(HkwsService_TR)|(HkwsService_TJ)|(HkwsService_JJ)|(ZywlTicketService)|(ZywlGateInfoService))")){
                            logger.warn("registerFromContext->>注册方法未授权访问 " + method.getDeclaringClass().getName() + "$" + method.getName());
                            return;
                        }

                        /**
                         * 2, 方法设置参数不匹配
                         */
                        if (method.getParameterTypes().length > 1) {
                            logger.warn("registerFromContext->>多个方法（{ }）的输入参数，不合格的Xbox规范, " + method.getDeclaringClass().getName() + "$" + method.getName());
                            return;
                        }
                        if (method.getParameterTypes().length == 1) {
                            paramType = method.getParameterTypes()[0];
                            if (!ClassUtils.isAssignable(XboxRequest.class, paramType)) {
                                logger.warn("registerFromContext->>输入参数没有继承XboxRequest基类,不合符规范,忽略此方法" + method.getDeclaringClass().getName() + "$" + method.getName());
                                return;
                            }
                            implType = !(paramType.isAssignableFrom(XboxRequest.class) || paramType.isAssignableFrom(AbstractXboxRequest.class));
                        }


                        //1.构建ServiceMethodDefinition
                        ServiceMethodDefinition serviceMethodDefinition = bulidServiceMethodDefinition(method);

                        //2.构建ServiceMethodHandler
                        ServiceMethodHandler serviceMethodHandler = new ServiceMethodHandler();
                        //设置requestType
                        serviceMethodHandler.setRequestType((Class<? extends XboxRequest>) paramType);
                        //设置method
                        serviceMethodHandler.setMethod(method);
                        //设置handler
                        serviceMethodHandler.setHandler(applicationContext.getBean(beanName));
                        //设置XboxRequestImplType
                        serviceMethodHandler.setXboxRequestImplType(implType);
                        //设置ServiceMethodDefinition
                        serviceMethodHandler.setServiceMethodDefinition(serviceMethodDefinition);


                        //3.添加到serviceMethods集合中
                        addServiceMethod(serviceMethodDefinition.getMethod(), serviceMethodDefinition.getVersion(), serviceMethodHandler);

                        logger.info("registerFromContext->>注册方法：" + method.getDeclaringClass().getName() + ":" + ServiceMethodDefinition.methodWithVerion(serviceMethodDefinition.getMethod(), serviceMethodDefinition.getVersion()));
                    }
                }, new ReflectionUtils.MethodFilter() {
                    @Override
                    public boolean matches(Method method) {
                        return (method.getDeclaringClass().getAnnotation(ServiceProvider.class) != null) && (AnnotationUtils.findAnnotation(method, ServiceMethod.class) != null);
                    }
                }
            );
        }
        //递归计算所有父容器中的
        if (applicationContext.getParent() != null) {
            registerFromContext(applicationContext.getParent());
        }
    }



    /**
     * 构建ServiceMethodDefinition
     * 从注解生成方法定义,先获取类上的配置,若方法上也配置则直接覆盖掉.
     * @param method
     * @return
     */
    @Override
    public ServiceMethodDefinition bulidServiceMethodDefinition(Method method) {
        ServiceProvider serviceProvider = method.getDeclaringClass().getAnnotation(ServiceProvider.class);
        ServiceMethod serviceMethod = method.getAnnotation(ServiceMethod.class);
        //先获取类上的标记
        ServiceMethodDefinition serviceMethodDefinition = new ServiceMethodDefinition();
        serviceMethodDefinition.setGroup(serviceProvider.group());
        serviceMethodDefinition.setGropupTitle(serviceProvider.groupTitle());
        serviceMethodDefinition.setHttpActions(serviceProvider.httpAction());
        serviceMethodDefinition.setTags(serviceProvider.tags());
        serviceMethodDefinition.setIgnoreSign(IgnoreSignType.isIgnoreSign(serviceProvider.ignoreSignType()));
        serviceMethodDefinition.setNeedInSession(NeedInSessionType.isNeedInSession(serviceProvider.needInSession()));
        serviceMethodDefinition.setObsoleted(ObsoletedType.isObsoleted(serviceProvider.obsoleted()));
        serviceMethodDefinition.setVersion(serviceProvider.version());
        serviceMethodDefinition.setMethod(serviceMethod.method());
        serviceMethodDefinition.setTitle(serviceMethod.title());
        //不同则覆盖
        serviceMethodDefinition.setVersion(serviceMethod.version());
        serviceMethodDefinition.setHandlerClass(method.getDeclaringClass());

        if (!ServiceMethodDefinition.DEFAULT_GROUP.equals(serviceMethod.group())) {
            serviceMethodDefinition.setGroup(serviceMethod.group());
        }

        if (!ServiceMethodDefinition.DEFAULT_GROUP_TITLE.equals(serviceMethod.groupTitle())) {
            serviceMethodDefinition.setGropupTitle(serviceMethod.groupTitle());
        }

        if (serviceMethod.tags() != null && serviceMethod.tags().length > 0) {
            serviceMethodDefinition.setTags(serviceMethod.tags());
        }

        if (serviceMethod.timeout() > 0) {
            serviceMethodDefinition.setTimeout(serviceMethod.timeout());
        }

        if (serviceMethod.ignoreSign() != IgnoreSignType.YES) {
            serviceMethodDefinition.setIgnoreSign(IgnoreSignType.isIgnoreSign(serviceMethod.ignoreSign()));
        }

        if (StringUtils.hasText(serviceMethod.version())) {
            serviceMethodDefinition.setVersion(serviceMethod.version());
        }

        if (serviceMethod.needInSession() != NeedInSessionType.NO) {
            serviceMethodDefinition.setNeedInSession(NeedInSessionType.isNeedInSession(serviceMethod.needInSession()));
        }

        if (serviceMethod.obsoleted() != ObsoletedType.NO) {
            serviceMethodDefinition.setObsoleted(ObsoletedType.isObsoleted(serviceMethod.obsoleted()));
        }

        if (serviceMethod.httpAction().length > 0) {
            serviceMethodDefinition.setHttpActions(serviceMethod.httpAction());
        }
            return serviceMethodDefinition;
    }
}

